/*
 * Copyright 2023-2025, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * 龙芯3A6000 LIOINTC本地I/O中断控制器驱动
 * 参考Linux的drivers/irqchip/irq-loongson-liointc.c实现
 */

#include "loongson_liointc.h"

#include <KernelExport.h>
#include <SupportDefs.h>
#include <arch/cpu.h>
#include <arch/loongarch64/arch_int.h>
#include <util/kernel_cpp.h>

#define TRACE_LOONGSON_LIOINTC
#ifdef TRACE_LOONGSON_LIOINTC
#define TRACE(x...) dprintf("loongson_liointc: " x)
#else
#define TRACE(x...)
#endif

#define ERROR(x...) dprintf("loongson_liointc: " x)

// 全局中断控制器实例
static loongson_liointc *sLIOINTC = NULL;

// 寄存器读写函数
static inline uint32
read_liointc_reg(addr_t base, uint32 reg)
{
    return *(volatile uint32 *)(base + reg);
}

static inline void
write_liointc_reg(addr_t base, uint32 reg, uint32 val)
{
    *(volatile uint32 *)(base + reg) = val;
}

// 中断控制器初始化
status_t
loongson_liointc_init(addr_t base_addr, uint32 irq_base)
{
    TRACE("Initializing Loongson LIOINTC at %#" B_PRIxADDR "\n", base_addr);

    // 分配中断控制器结构体
    sLIOINTC = new (std::nothrow) loongson_liointc;
    if (sLIOINTC == NULL) {
        ERROR("Failed to allocate loongson_liointc structure\n");
        return B_NO_MEMORY;
    }

    // 初始化结构体
    memset(sLIOINTC, 0, sizeof(loongson_liointc));
    sLIOINTC->base_addr = base_addr;
    sLIOINTC->irq_base = irq_base;
    sLIOINTC->num_irqs = LOONGSON_LIOINTC_MAX_IRQS;

    // 禁用所有中断
    write_liointc_reg(base_addr, LOONGSON_LIOINTC_IEN, 0);
    sLIOINTC->irq_enable = 0;

    // 初始化中断映射
    // 默认将所有中断映射到优先级0
    for (int i = 0; i < 4; i++) {
        write_liointc_reg(base_addr, LOONGSON_LIOINTC_MAP0 + i * 4, 0);
        sLIOINTC->irq_map[i] = 0;
    }

    // 配置特殊中断
    // 将定时器中断映射到优先级3（最高）
    loongson_liointc_configure_interrupt(LOONGSON_LIOINTC_TIMER_IRQ, LOONGSON_LIOINTC_PRIO_3);
    
    // 将IPI中断映射到优先级2
    loongson_liointc_configure_interrupt(LOONGSON_LIOINTC_IPI_IRQ, LOONGSON_LIOINTC_PRIO_2);

    // 将UART中断映射到优先级1
    loongson_liointc_configure_interrupt(LOONGSON_LIOINTC_UART_IRQ, LOONGSON_LIOINTC_PRIO_1);

    TRACE("LIOINTC initialized with %lu interrupts\n", sLIOINTC->num_irqs);
    return B_OK;
}

// 中断控制器反初始化
void
loongson_liointc_uninit(void)
{
    if (sLIOINTC != NULL) {
        // 禁用所有中断
        write_liointc_reg(sLIOINTC->base_addr, LOONGSON_LIOINTC_IEN, 0);
        
        // 释放结构体
        delete sLIOINTC;
        sLIOINTC = NULL;
    }
}

// 启用中断
status_t
loongson_liointc_enable_interrupt(uint32 irq)
{
    if (sLIOINTC == NULL)
        return B_NO_INIT;

    if (irq >= sLIOINTC->num_irqs)
        return B_BAD_VALUE;

    // 设置中断使能位
    uint32 mask = 1 << irq;
    sLIOINTC->irq_enable |= mask;
    write_liointc_reg(sLIOINTC->base_addr, LOONGSON_LIOINTC_IEN, sLIOINTC->irq_enable);

    TRACE("Enabled interrupt %lu\n", irq);
    return B_OK;
}

// 禁用中断
status_t
loongson_liointc_disable_interrupt(uint32 irq)
{
    if (sLIOINTC == NULL)
        return B_NO_INIT;

    if (irq >= sLIOINTC->num_irqs)
        return B_BAD_VALUE;

    // 清除中断使能位
    uint32 mask = 1 << irq;
    sLIOINTC->irq_enable &= ~mask;
    write_liointc_reg(sLIOINTC->base_addr, LOONGSON_LIOINTC_IEN, sLIOINTC->irq_enable);

    TRACE("Disabled interrupt %lu\n", irq);
    return B_OK;
}

// 配置中断优先级
status_t
loongson_liointc_configure_interrupt(uint32 irq, uint32 priority)
{
    if (sLIOINTC == NULL)
        return B_NO_INIT;

    if (irq >= sLIOINTC->num_irqs || priority > LOONGSON_LIOINTC_PRIO_3)
        return B_BAD_VALUE;

    // 计算寄存器索引和位偏移
    uint32 reg_idx = irq / 8;
    uint32 bit_idx = (irq % 8) * 4;

    // 更新映射寄存器
    uint32 map_val = sLIOINTC->irq_map[reg_idx];
    map_val &= ~(0xf << bit_idx);  // 清除旧的优先级
    map_val |= (priority << bit_idx);  // 设置新的优先级
    sLIOINTC->irq_map[reg_idx] = map_val;

    // 写入硬件寄存器
    write_liointc_reg(sLIOINTC->base_addr, LOONGSON_LIOINTC_MAP0 + reg_idx * 4, map_val);

    TRACE("Configured interrupt %lu with priority %lu\n", irq, priority);
    return B_OK;
}

// 中断处理函数
int32
loongson_liointc_handle_interrupt(void *data)
{
    if (sLIOINTC == NULL)
        return B_HANDLED_INTERRUPT;

    // 读取中断状态寄存器
    uint32 status = read_liointc_reg(sLIOINTC->base_addr, LOONGSON_LIOINTC_ISR);
    uint32 enabled = status & sLIOINTC->irq_enable;

    if (enabled == 0)
        return B_UNHANDLED_INTERRUPT;

    // 处理所有活跃的中断
    for (uint32 i = 0; i < sLIOINTC->num_irqs; i++) {
        if (enabled & (1 << i)) {
            uint32 irq = sLIOINTC->irq_base + i;
            
            // 调用Haiku中断处理系统
            int_io_interrupt_handler(irq, true);
            
            // 清除中断状态
            // 对于LIOINTC，写1清除对应的中断状态位
            write_liointc_reg(sLIOINTC->base_addr, LOONGSON_LIOINTC_ISR, 1 << i);
        }
    }

    return B_HANDLED_INTERRUPT;
}

// 发送处理器间中断(IPI)
status_t
loongson_liointc_send_ipi(uint32 target_cpu)
{
    if (sLIOINTC == NULL)
        return B_NO_INIT;

    // 龙芯处理器使用特殊的IPI寄存器发送IPI
    // 这里需要根据龙芯3A6000的具体实现来完成
    // 暂时使用简单的实现，通过写入IPI控制寄存器触发IPI
    
    // 假设IPI控制寄存器位于LIOINTC基地址之后的特定偏移
    addr_t ipi_addr = sLIOINTC->base_addr + 0x100 + target_cpu * 4;
    *(volatile uint32 *)ipi_addr = 1;

    TRACE("Sent IPI to CPU %lu\n", target_cpu);
    return B_OK;
}

// 发送广播处理器间中断(IPI)
status_t
loongson_liointc_send_broadcast_ipi(void)
{
    if (sLIOINTC == NULL)
        return B_NO_INIT;

    // 假设广播IPI控制寄存器位于特定偏移
    addr_t ipi_broadcast_addr = sLIOINTC->base_addr + 0x200;
    *(volatile uint32 *)ipi_broadcast_addr = 1;

    TRACE("Sent broadcast IPI\n");
    return B_OK;
}

// 模块导出函数
static status_t
std_ops(int32 op, ...)
{
    switch (op) {
        case B_MODULE_INIT:
            // 模块初始化在系统启动时由内核调用
            return B_OK;
        case B_MODULE_UNINIT:
            // 模块卸载时清理资源
            loongson_liointc_uninit();
            return B_OK;
        default:
            return B_ERROR;
    }
}

// 中断控制器接口实现
static status_t
get_interrupt_controller(interrupt_controller **controller, uint32 id)
{
    // 暂时只支持一个LIOINTC实例
    if (id != 0 || sLIOINTC == NULL)
        return B_BAD_VALUE;

    // 创建中断控制器接口
    static interrupt_controller liointc_controller = {
        .enable_io_interrupt = loongson_liointc_enable_interrupt,
        .disable_io_interrupt = loongson_liointc_disable_interrupt,
        .configure_io_interrupt = loongson_liointc_configure_interrupt,
        .send_ipi = loongson_liointc_send_ipi,
        .send_broadcast_ipi = loongson_liointc_send_broadcast_ipi,
    };

    *controller = &liointc_controller;
    return B_OK;
}

// 导出模块接口
static interrupt_controller_module_info sLiointcModuleInfo = {
    {
        B_INTERRUPT_CONTROLLER_MODULE_NAME,
        0,
        std_ops
    },
    get_interrupt_controller
};

module_info *modules[] = {
    (module_info *)&sLiointcModuleInfo,
    NULL
};